//	------------------------------------------------------------------------------------------------
//	File name:		PdbModule.cpp
//	Author:			Marc Ochsenmeier
//	Email:			marc@ochsenmeier.de
//	Web:			www.breakpoints.de - 
//	Date:			02.07.2009
//
//	Description:	Implementation of the Pdb Module class.
//
//	------------------------------------------------------------------------------------------------
#include "stdafx.h"

#include "PdbTranslationMaps.h"
#include "PdbModule.h"
#include "PdbParser.h"
#include "PdbParserInternal.h"
#include "PdbModuleDetails.h"
#include "PdbSourceFile.h"
#include "PdbFile.h"

//	Ctor
CPdbModule::CPdbModule(CPdbFile* pPdbFile, IDiaSymbol* pIDiaSymbol) : 
	m_pPdbFile(pPdbFile),
	m_pIDiaSymbol(pIDiaSymbol),
	m_pPdbCompilandDetails(NULL)
{
}
CPdbModule::~CPdbModule()
{
	//	Cleanup Children Symbols collection
	std::vector<IPdbModule*>::iterator it1 = m_vectorModules.begin();
	for( ;it1!=m_vectorModules.end(); it1++)
	{
		CPdbModule* pPdbSymbol = (CPdbModule*) *it1;
		delete pPdbSymbol;
	}
	m_vectorModules.clear();

	//	Cleanup Source Files collection
	std::vector<IPdbSourceFile*>::iterator it2 = m_vectorSources.begin();
	for( ;it2!=m_vectorSources.end(); it2++)
	{
		CPdbSourceFile* pPdbSymbol = (CPdbSourceFile*) *it2;
		delete pPdbSymbol;
	}
	m_vectorSources.clear();

	if(m_pIDiaSymbol)
	{
		m_pIDiaSymbol->Release();
		m_pIDiaSymbol = NULL;
	}
	delete m_pPdbCompilandDetails;
	m_pPdbCompilandDetails = NULL;
}
std::vector<IPdbModule*>& CPdbModule::GetModules(PdbSymbolType type)
{
	//	Cleanup Children Symbols collection
	std::vector<IPdbModule*>::iterator it = m_vectorModules.begin();
	for( ;it!=m_vectorModules.end(); it++)
	{
		CPdbModule* pPdbSymbol = (CPdbModule*) *it;
		delete pPdbSymbol;
	}
	m_vectorModules.clear();

	if(m_pIDiaSymbol)
	{
		//	Map input to DIA type
		enum SymTagEnum symbol = CPdbTranslationMaps::MapSymbolToDiaType(type);

		IDiaEnumSymbols* pIDiaEnumSymbols = NULL;
		HRESULT hr = m_pIDiaSymbol->findChildren(symbol, NULL, nsNone, &pIDiaEnumSymbols);
		if(hr==S_OK)
		{
			LONG lCount = 0;
			hr = pIDiaEnumSymbols->get_Count(&lCount);
			
			IDiaSymbol* pIDiaSymbol = NULL;
			for(DWORD dwPos=0; dwPos<(DWORD)lCount; dwPos++)
			{
				hr = pIDiaEnumSymbols->Item(dwPos, &pIDiaSymbol);
				if(hr==S_OK)
				{
					CPdbModule* pSymbol = new CPdbModule(m_pPdbFile, pIDiaSymbol);
					
					//	Keep this symbol in the List? (e.g. Reject Symbol beginning with "Import:")
					if(pSymbol->Reject())
					{
						delete pSymbol;
						pSymbol = NULL;
					}
					else
					{
						m_vectorModules.push_back(pSymbol);
					}
				}
			}
			pIDiaEnumSymbols->Release();
		}
	}
	return m_vectorModules;
}
std::vector<IPdbSourceFile*>& CPdbModule::GetSourceFiles()
{
	//	Cleanup Children Symbols collection
	std::vector<IPdbSourceFile*>::iterator it = m_vectorSources.begin();
	for( ;it!=m_vectorSources.end(); it++)
	{
		CPdbSourceFile* pPdbSource = (CPdbSourceFile*) *it;
		delete pPdbSource;
	}
	m_vectorSources.clear();
	std::vector<DWORD> vecLines;

	if(m_pIDiaSymbol)
	{
		IDiaSession* pIDiaSession = m_pPdbFile->GetDiaSession();
		if(pIDiaSession)
		{
			IDiaEnumSourceFiles *pEnumSourceFiles = NULL;
			if(pIDiaSession->findFile(m_pIDiaSymbol, NULL, nsNone, &pEnumSourceFiles)==S_OK)
			{
				LONG lCount = 0;
				pEnumSourceFiles->get_Count(&lCount);

				for(DWORD dw = 0; dw < (DWORD)lCount; ++dw)
				{
					IDiaSourceFile* pIDiaSourceFile = NULL;
					if(pEnumSourceFiles->Item(dw, &pIDiaSourceFile) == S_OK)
					{
						IDiaEnumLineNumbers* pEnumLineNumbers = NULL;

						CPdbSourceFile* pPdbSourceFile = new CPdbSourceFile(this, pIDiaSourceFile);
						m_vectorSources.push_back(pPdbSourceFile);
						
						if (pIDiaSession->findLines(m_pIDiaSymbol, pIDiaSourceFile, &pEnumLineNumbers) == S_OK)
						{
							LONG lLinesCount = 0;
							pEnumLineNumbers->get_Count(&lLinesCount);

							for (DWORD dwIndex = 0; dwIndex < (DWORD)lLinesCount; ++dwIndex)
							{
								IDiaLineNumber* pIDiaLineNumber = NULL;
								if (pEnumLineNumbers->Item(dwIndex, &pIDiaLineNumber) == S_OK)
								{
									DWORD dwNumber = 0;
									pIDiaLineNumber->get_lineNumber(&dwNumber);
									pPdbSourceFile->AddLineNumber(dwNumber);
								}

								if (pIDiaLineNumber)
								{
									pIDiaLineNumber->Release();
									pIDiaLineNumber = NULL;
								}
							}
						}

						if (pEnumLineNumbers)
						{
							pEnumLineNumbers->Release();
							pEnumLineNumbers = NULL;
						}
					}
				}
			}
			pEnumSourceFiles->Release();
		}
	}
	return m_vectorSources;
}

IPdbModuleDetails* CPdbModule::GetModuleDetails()
{	
	//	Cleanup from any previous call
	delete m_pPdbCompilandDetails;
	m_pPdbCompilandDetails = NULL;

	//	Create output object
	m_pPdbCompilandDetails = new CPdbModuleDetails(this);

	std::vector<IPdbModule*>vSymbols = GetModules(PdbSymbolType_CompilandDetails);
	std::vector<IPdbModule*>::iterator it = vSymbols.begin();
	for( ;it!=vSymbols.end(); it++)
	{
		CPdbModule* pSymbol = (CPdbModule*)*it;
		m_pPdbCompilandDetails->SetCompilerName(pSymbol->GetCompilerName());
		m_pPdbCompilandDetails->SetGetBackEndBuildNumber(pSymbol->GetBackEndBuildNumber());
		m_pPdbCompilandDetails->SetManagedCode(pSymbol->GetManagedCode());
	}
	return m_pPdbCompilandDetails;
}
DWORD CPdbModule::GetTypeId()
{
	DWORD dwRet = -1;
	if(m_pIDiaSymbol)
	{
		HRESULT hr = m_pIDiaSymbol->get_typeId(&dwRet);
		dwRet = hr==S_OK?dwRet:-1;
	}
	return dwRet;
}

const std::wstring& CPdbModule::GetName()
{
	m_sSymbolName = L"";
	if(m_pIDiaSymbol)
	{
		BSTR bstrName;
		HRESULT hr = m_pIDiaSymbol->get_name(&bstrName);
		if(hr==S_OK)
		{
			m_sSymbolName = bstrName;
			SysFreeString(bstrName);
		}
	}
	return m_sSymbolName;
}
bool CPdbModule::Reject()
{
	bool bRet = false;
	wstring sName = GetName();
	CPdbTranslationMaps::MapSymbolsToReject::iterator it = CPdbTranslationMaps::mapSymbolsToReject.begin();
	wstring sItemRejected;
	size_t iPos = wstring::npos;
	for( ;it!=CPdbTranslationMaps::mapSymbolsToReject.end(); it++)
	{
		sItemRejected = it->first;
		iPos = sName.find(sItemRejected);
		if(iPos!=wstring::npos)
		{
			bRet = true;
			break;
		}
	}
	return bRet;
}
wstring CPdbModule::GetCompilerName()
{
	wstring sRet;
	if(m_pIDiaSymbol)
	{
		BSTR bstrName;
		HRESULT hr = m_pIDiaSymbol->get_compilerName(&bstrName);
		if(hr==S_OK)
		{
			sRet = bstrName;
			SysFreeString(bstrName);
		}
	}
	return sRet;
}
std::wstring CPdbModule::GetBackEndBuildNumber()
{
	wstring sRet;	
	if(m_pIDiaSymbol)
	{
		DWORD dwVerMajor, dwVerMinor, dwVerBuild = 0;
		if ((m_pIDiaSymbol->get_backEndMajor(&dwVerMajor) == S_OK) &&
			(m_pIDiaSymbol->get_backEndMinor(&dwVerMinor) == S_OK) &&
			(m_pIDiaSymbol->get_backEndBuild(&dwVerBuild) == S_OK)) 
		{
			ZeroMemory(m_buffer, 64*sizeof(TCHAR));
			int iSize = swprintf(m_buffer, L"<%u>.<%u>.<%u>", dwVerMajor, dwVerMinor, dwVerBuild); 
			sRet.assign(m_buffer);
		}
	}
	return sRet;
}
bool CPdbModule::GetManagedCode()
{
	bool bRet = false;
	if(m_pIDiaSymbol)
	{
		BOOL b = FALSE;
		HRESULT hr = m_pIDiaSymbol->get_managed(&b);
		bRet = hr==S_OK?true:false;
	}
	return bRet;
}